
#include "ProgramElem.h"


#include "Type.h"


#include "SymTabMgr.h"

#include "STEClasses.h"
#include "Value.h"
#include "ParserUtil.h"
bool first_fun = true;
extern vector<three_addr_ins*> inter_code;

/****************************************************************/
const Type* GlobalEntry::typeCheck() 
{
    //aripio: check symtab entry
    this->symTab()->typeCheck();
    
    //william: check the rule nodes    
    vector<RuleNode*> rules = this->rules_;
    for(vector<RuleNode*>::iterator it = rules.begin(); it != rules.end(); ++it)
    {
        RuleNode *r = *it;
        r->typeCheck();
    }
    cout<<endl;
}

void GlobalEntry::print(ostream& out, int indent)const 
{
	vector<RuleNode*>::const_iterator  iter;

	this->symTab()->printST(out,indent,' ',' ');
	out<<endl;
	for(iter = rules_.begin(); iter != rules_.end(); iter++)
	{
		(*iter)->print(out,indent);
		out<<endl;
	}
	if(rules_.empty())
		out<<endl;
	out<<endl;

}

void GlobalEntry::typePrint(ostream& out, int indent) const 
{
	/* Fill in yours */
	vector<RuleNode*>::const_iterator  iter;

	this->symTab()->typePrintST(out,indent,' ',' ');
	out<<endl;
	for(iter = rules_.begin(); iter != rules_.end(); iter++)
	{
		(*iter)->typePrint(out,indent);
		out<<endl;
	}
	if(rules_.empty())
		out<<endl;
	out<<endl;
}

void GlobalEntry::genCode(ostream& out, int indent) const
{

	vector<RuleNode*>::const_iterator iter;
	for(iter = rules_.begin(); iter != rules_.end(); iter++)
	{
		(*iter)->genCode();
		out<<endl;
	}
}

void GlobalEntry::genCode3(ostream& out, int indent) const
{
	SymTab::const_iterator iter1 = this->symTab()->begin();

	//vector<SymTabEntry*>::const_iterator iter1;
	for(; iter1 != this->symTab()->end(); ++iter1)
	{
		if((*iter1)->kind()==SymTabEntry::FUNCTION_KIND)
			((FunctionEntry*)(*iter1))->genCode3(out, indent);
		else if((*iter1)->kind() == SymTabEntry::VARIABLE_KIND)
			((VariableEntry*)(*iter1))->genCode3(out,indent);
	}


	vector<RuleNode*>::const_iterator iter;
	for(iter = rules_.begin(); iter != rules_.end(); iter++)
	{
		(*iter)->genCode3();
	}
}
/****************************************************************/
const Type* EventEntry::typeCheck() {/* Fill in yours */}
void EventEntry::print(ostream&, int indent) const {/* Fill in yours */}
void EventEntry::typePrint(ostream& os, int indent) const
{/* Fill in yours */
	//xiaohua hou
	const SymTab* temp = symTab();

	os<<"event"<<" ";
	os<<this->name();
	os<<"(";

	if(temp)
	{
		int i = 1;
		SymTab::const_iterator it = temp->begin();
		do{
			SymTabEntry *ste = (SymTabEntry *)(*it);
			ste->typePrint(os, indent);
			if(i != temp->size())
				os<<", ";
			++it;
			++i;
		}while(it != temp->end());
	}

	os<<")"<<";";
}
/****************************************************************/
const Type* ClassEntry::typeCheck() {/* Fill in yours */}

void ClassEntry::print(ostream& os, int indent)const
{
	os<<"class"<<" "<<this->name()<<";";
}

void ClassEntry::typePrint(ostream& os, int indent) const {
    //william
    os<<"class"<<" "<<this->name();
    os<<";";

}
/****************************************************************/
VariableEntry::VariableEntry(const VariableEntry &v):SymTabEntry(v.name(),v.kind(),0,0,""){

    
}

void VariableEntry::print(ostream& os, int indent)const
{
	os<<this->type()->name()<<" ";
	if(this->type()->tag() == Type::CLASS)
		os<<this->type()->typeDesc()->name()<<" ";
	os<<this->name();
	if(initVal_ != NULL)
	{
		os<<" = ";
		initVal_->print(os,indent);
	}
	if(this->varKind() != VariableEntry::PARAM_VAR)
		cout<<";";	
}

const Type* VariableEntry::typeCheck(){
    //william
    if(this->initVal() != NULL)
    {
    	const Type *t = this->initVal()->type();
    	
    
    if(t->tag() == Type::CLASS)
    {
    	if (this->type()->tag() != Type::CLASS
    	|| (this->type()->tag() == Type::CLASS && this->type()->typeDesc()->name() != ((RefExprNode*)this->initVal())->symTabEntry()->type()->typeDesc()->name()))
    		errMsg("Assignment between incompatible types",line(),0,file().c_str());
    	
    }
    else
    	if (!t->isSubType(this->type()->tag())) 
    {
        errMsg("Assignment between incompatible types",line(),0,file().c_str());
    }
    }

}

void VariableEntry::typePrint(ostream& os, int indent) const 
{
	//os<<"VaPrint: ";
	os<<this->type()->name()<<" ";
	if(this->type()->tag() == Type::CLASS)
		os<<this->type()->typeDesc()->name()<<" ";
	os<<this->name();
	if(initVal_ != NULL)
	{
		os<<" = ";
		initVal_->typePrint(os,indent);
	}
	//if(this->varKind() != VariableEntry::PARAM_VAR)
		//cout<<";";
}

void VariableEntry::genCode3(ostream& os, int indent)
{
	code3_= new three_addr_ins ();
	addr3_= new address();
	addr3_->atype = address::VARI;
	addr3_->ve = this;	
	if(initVal() != NULL)
	{
		{
		initVal()->genCode3();
		code3_->arity = 1;
		code3_-> oprand1 = initVal()->addr3();
		code3_-> dest = addr3_;
		switch(this->type()->tag())
		{
		case Type::BOOL:
		case Type::INT:
		case Type::UINT:
		case Type::BYTE:
			if(code3_-> oprand1->atype != address::CONST_DOUBLE && code3_->oprand1->atype != address::TEMP_FLOAT)			
				code3_-> op = "MOVI";
			else code3_-> op = "MOVFI";
			break;
		case Type::DOUBLE:
			if(code3_-> oprand1->atype == address::CONST_DOUBLE || code3_->oprand1->atype == address::TEMP_FLOAT)
				code3_-> op = "MOVF";
			else code3_-> op = "MOVIF";
			break;
		case Type::STRING:
			if(initVal()->addr3()->atype == address::CONST_STR)			
			code3_-> op = "MOVS";
			else
			code3_-> op = "MOVI";
		}
	}
	inter_code.push_back(code3_);
	}
}

/****************************************************************/
const Type* BlockEntry::typeCheck(){}

void BlockEntry::print(ostream& out, int indent) const
{
	if(this->symTab())
	  this->symTab()->printST(out,indent);
	
}

void BlockEntry::typePrint(ostream& os, int indent) const{}
/****************************************************************/ 
const Type* FunctionEntry::typeCheck()
{
    
	if(this->name() == "print")
		return new Type(Type::VOID);
	if(this->body() != NULL)
		this->body()->typeCheck();

	//formals_ = new vector<const VaribaleEntry*>; 
    //vector<RuleNode*> rules = this->rules_;
    //for(vector<RuleNode*>::iterator it = rules.begin(); it != rules.end(); ++it)
    /*
    vector<VariableEntry*> *args = this->formals_;
    
    for(vector<VariableEntry*>::iterator it = args.begin(); it != args.end(); it++)
    {
        VariableEntry *r = *it;
        cout<<r->type()->fullName()<<endl;
    }
     */
    if(this->symTab() == NULL)
	return this->type()->retType();;

    SymTab::iterator iter = this->symTab()->begin();

    for( ; iter!=this->symTab()->end(); ++iter){
		if((*iter)->kind() == SymTabEntry::VARIABLE_KIND && ((VariableEntry*)(*iter))->varKind() == VariableEntry::PARAM_VAR)
		{
			formals_.push_back((VariableEntry*)(*iter));
			//cout<<(*iter)->name()<<endl;
		}
	}
    return this->type()->retType();
}


void FunctionEntry::print(ostream& os, int indent) const
{
	os<<this->type()->retType()->name()<<" "<<this->name();
	bool bc = false;
	if(this->body() == NULL)
	{
	if(this->symTab())
	  this->symTab()->printST(os,indent,'(',')',false);
	else 
		os<<"("<<")";
	}
	else
	{
		
		int i = 1;
		SymTab::const_iterator iter(*(this->symTab()->begin()));
		for(; iter != *(this->symTab()->end()); ++iter)
		{	if((*iter)->kind() == SymTabEntry::BLOCK_KIND)
			{
				
				break;
			}
				
			else i++;
		}
				
		if(!((*iter)->kind() == SymTabEntry::BLOCK_KIND && i == 1))
			this->symTab()->printST(os,indent,'(',')',false,0,i-1);
		
		else
		{
			os<<"("<<")";
		}		
		os<<"{"<<endl;
		if((*iter)->symTab() != NULL)
		(*iter)->symTab()->printST(os,indent,'\0','\0');
		
		this->body()->printWithoutBraces(os,indent);
		os<<"}";
	}
	os<<";";
}

void FunctionEntry::typePrint(ostream& os, int indent) const{

	//Ying
	//this->print(os,0);
	if(this->type()){
		if(this->type()->retType())
			os<<this->type()->retType()->name()<<" "; //print return type

		os<<this->name();

		if(this->body()&&this->symTab()){  //has body {  }
			os<<"(";

			int i = 0;
			SymTab::const_iterator iter = this->symTab()->begin();

			for( ; iter!=this->symTab()->end(); ++iter){
				(*iter)->typePrint(os,indent);
				i++;
				//os<<i<<" "<<this->symTab()->size();
				if(i<this->symTab()->size()-1)
					os<<", ";
			}
			os<<")";

			os<<"{"<<endl;
			this->body()->typePrintWithoutBraces(os,indent);
			os<<"}";

		}

		else if(this->symTab()){ //without body, print parameters
			this->symTab()->typePrintST(os,indent,'(',')',false) ;
		}
		else
			os<<"()";
		  //const vector<const Type*>* args ;
		//args = this->type()->argTypes();
        //if (this->body_!= NULL)
        	//this->symTab()->typePrintST(os,0,'(',')',false,0,999);

		os<<";";
	}
}

void FunctionEntry::genCode(ostream& os, int indent ) const{
	SymTab::const_iterator iter = this->symTab()->begin();

	for( ; iter!=this->symTab()->end(); ++iter){
		(*iter)->typePrint(os,indent);

	}

}

void FunctionEntry::genCode3(ostream& os, int indent ) {
	//os<<this->name()<<" FuncgenCode3"<<endl;
	//DFA JMP
	if (first_fun) {
		//inter_code.push_back(genGotoLabel(string("LOOP")));
		first_fun = false;
	}
	code_= new three_addr_ins ();
	addr_= new address ();
	if(this->body()!=NULL){
	code_->label = const_cast<char*>(this->name().c_str()) ;
	code_->arity = three_addr_ins::FUN_LABEL;
	addr_->fe = this;
	inter_code.push_back(code_);

	vector<VariableEntry*>::iterator iter2;
	int i = 0;
	for(iter2 = this->formals()->begin(); iter2 != this->formals()->end(); iter2++,i++)
	{
		three_addr_ins* stmt  = new three_addr_ins();
		if(i!=0)
		stmt -> op = "RPARAM";
		else
		stmt -> op = "FRPARAM";
		stmt -> arity = three_addr_ins::REALPARAM;
		stmt -> oprand1 = new address();
		stmt -> oprand1 -> atype = address::VARI;
		stmt -> oprand1 -> ve = *iter2; 
		inter_code.push_back(stmt);
	}
	

	//vector<CompoundStmtNode*>::iterator iter;
	list<StmtNode*>::iterator iter;
	
	for(iter=this->body()->stmts()->begin();iter!=body()->stmts()->end();iter++ ){

		three_addr_ins *stmt = new three_addr_ins ();

		(*iter)->genCode3();

	}
	
	three_addr_ins* return_ins = new three_addr_ins();
	return_ins-> op = "RETURN";
	return_ins-> arity = 1;
	inter_code.push_back(return_ins);
	}
}

/****************************************************************/
